package org.tinygame.herostory.cmdHandle;

import com.google.protobuf.GeneratedMessageV3;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tinygame.herostory.util.PackageUtil;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * 指令处理器工厂
 */
public final class CmdHandlerFactory {
    /**
     * 日志对象
     */
    private static final Logger LOGGER = LoggerFactory.getLogger(CmdHandlerFactory.class);

    /**
     * 处理器字典
     */
    private static final Map<Class<?>, ICmdHandler<? extends GeneratedMessageV3>> _handleMap = new HashMap<>();

    /**
     * 私有化类默认构造器
     */
    private CmdHandlerFactory() {
    }

    public static void init(){
        //获取CmdHandlerFactory所在包下的所有实现了ICmdHandler接口的类
        Set<Class<?>> clazzSet = PackageUtil.listSubClazz(
                CmdHandlerFactory.class.getPackage().getName(),
                true,
                ICmdHandler.class);

        for (Class<?> clazz : clazzSet) {
            if ((clazz.getModifiers()& Modifier.ABSTRACT) != 0){
                //如果存在修饰符abstract，说明该类型是接口，排除掉
                continue;
            }
            //获取方法数组
            Method[] methodArray = clazz.getDeclaredMethods();

            //消息类型
            Class<?> msgType = null;
            for (Method currMethod : methodArray) {
                if (!currMethod.getName().equals("handle")){
                    continue;
                }
                //获取函数参数类型
                Class<?>[] paramTypeArray = currMethod.getParameterTypes();

                //校验参数列表长度和参数类型
                if (paramTypeArray.length < 2 || paramTypeArray[1] == GeneratedMessageV3.class || !GeneratedMessageV3.class.isAssignableFrom(paramTypeArray[1])) {
                    continue;
                }
                msgType = paramTypeArray[1];//拿到消息类型
                break;//中断循环
            }

            if (null == msgType) {
                continue;
            }

            try {
                //通过反射生成实例对象
                ICmdHandler<?> newHandler = (ICmdHandler<?>) clazz.newInstance();

                LOGGER.info("{} <===> {}",msgType.getName(),clazz.getName());


                _handleMap.put(msgType,newHandler);//动态添加到字典中
            } catch (Exception e) {
                LOGGER.error(e.getMessage(),e);
            }
        }
    }

    public static ICmdHandler<? extends GeneratedMessageV3> create(Class<?> msgClazz){
        if (null == msgClazz){
            return null;
        }
        return _handleMap.get(msgClazz);
    }
}
